/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/

#pragma once

#include <string>
#include <memory>
#include <db/db_error.h>
#include <db/db_interfaces.h>
#include <db/db_field_value_t.hpp>

namespace test_wrappers { namespace storage {

const unsigned int UnicodeCodePage = 1200;
const unsigned int ANSICodePage = 1252;
const unsigned int CurrentCodePage = UnicodeCodePage;
const unsigned int MaxStringSizeInBytes = 260 * sizeof(wchar_t);
const unsigned int MaxBinaryBlobSize = 1048576;

template<class T>
field_type type()
{
    return type_unknown;
}

template<>
field_type type<unsigned short>();

template<>
field_type type<unsigned int>();

template<>
field_type type<unsigned __int64>();

template<>
field_type type<float>();

template<>
field_type type<std::wstring>();

template<>
field_type type<binary_blob>();

template<class T>
class field_definition_t :
    public field_definition,
    public std::enable_shared_from_this<field_definition_t<T>>
{
public:
    field_definition_t(
        const field_id_t<T>& userId,
        const std::wstring& name) :
        _userId(userId),
        _name(name),
        _type(test_wrappers::storage::type<T>()),
        _maxBufferSizeInBytes(0),
        _fixed(true),
        _autoIncrement(false),
        _appendOnUpdate(false)
    {
    }

    static std::shared_ptr<field_definition_t<T>> create(
        const field_id_t<T>& userId,
        const std::wstring& name)
    {
        std::shared_ptr<field_definition_t<T>> returnValue = std::make_shared<field_definition_t<T>>(userId, name);
        return returnValue;
    }

    // field_definition
    void setUserId(const field_id_t<T>& userId)
    {
        _userId = userId;
    }

    void setFixed(bool fixed)
    {
        _fixed = fixed;
    }
    
    void setAutoIncrement(bool autoIncrement)
    {
        _autoIncrement = autoIncrement;
    }

    void setMaxBufferSizeInBytes(unsigned int sizeInBytes)
    {
        throw std::logic_error("field_definition_t::setMaxBufferSizeInBytes() is not supported for this type.");
    }

    field_id userId() const
    {
        return _userId.id;
    }

    field_id_t<T> userIdT() const
    {
        return _userId;
    }

    std::wstring name() const
    {
        return _name;
    }

    field_type type() const
    {
        return _type;
    }

    bool fixed() const
    {
        return _fixed;
    }

    bool autoIncrement() const
    {
        return _autoIncrement;
    }

    unsigned int codePage() const
    {
        return CurrentCodePage;
    }

    unsigned long maxBufferSizeInBytes() const
    {
        throw std::logic_error("field_definition_t::maxBufferSizeInBytes() is not supported for this type.");
        return 0;
    }

    std::shared_ptr<field_definition> clone() const
    {
        std::shared_ptr<field_definition_t<T>> c = std::make_shared<field_definition_t<T>>(_userId, _name);
        c->_maxBufferSizeInBytes = _maxBufferSizeInBytes;
        c->_fixed = _fixed;
        c->_autoIncrement = _autoIncrement;
        c->_appendOnUpdate = _appendOnUpdate;
        return c;
    }

protected:
    field_id_t<T> _userId;
    std::wstring _name;
    field_type _type;
    unsigned long _maxBufferSizeInBytes;
    bool _fixed;
    bool _autoIncrement;
    bool _appendOnUpdate;
};

class string_field : public field_definition_t<std::wstring>
{
public:
    string_field(
        const field_id_t<std::wstring>& userId,
        const std::wstring& name) :
        field_definition_t<std::wstring>(userId, name)
    {
        _fixed = false;
        _maxBufferSizeInBytes = MaxStringSizeInBytes;
    }
    static std::shared_ptr<string_field> create(
        const field_id_t<std::wstring>& userId,
        const std::wstring& name)
    {
        std::shared_ptr<string_field> returnValue = std::make_shared<string_field>(userId, name);
        return returnValue;
    }
    void setMaxBufferSizeInBytes(unsigned int sizeInBytes)
    {
        _maxBufferSizeInBytes = sizeInBytes;
    }
    unsigned long maxBufferSizeInBytes() const
    {
        return _maxBufferSizeInBytes;
    }
    std::shared_ptr<field_definition> clone() const
    {
        std::shared_ptr<string_field> c = std::make_shared<string_field>(_userId, _name);
        c->_maxBufferSizeInBytes = _maxBufferSizeInBytes;
        c->_fixed = _fixed;
        c->_autoIncrement = _autoIncrement;
        c->_appendOnUpdate = _appendOnUpdate;
        return c;
    }
};

class blob_field : public field_definition_t<binary_blob>
{
public:
    blob_field(
        const field_id_t<binary_blob>& userId,
        const std::wstring& name) :
        field_definition_t<binary_blob>(userId, name)
    {
        _fixed = false;
        _maxBufferSizeInBytes = MaxBinaryBlobSize;
    }
    static std::shared_ptr<blob_field> create(
        const field_id_t<binary_blob>& userId,
        const std::wstring& name)
    {
        std::shared_ptr<blob_field> returnValue = std::make_shared<blob_field>(userId, name);
        return returnValue;
    }
    void setMaxBufferSizeInBytes(unsigned int sizeInBytes)
    {
        _maxBufferSizeInBytes = sizeInBytes;
    }
    unsigned long maxBufferSizeInBytes() const
    {
        return _maxBufferSizeInBytes;
    }
    std::shared_ptr<field_definition> clone() const
    {
        std::shared_ptr<blob_field> c = std::make_shared<blob_field>(_userId, _name);
        c->_maxBufferSizeInBytes = _maxBufferSizeInBytes;
        c->_fixed = _fixed;
        c->_autoIncrement = _autoIncrement;
        c->_appendOnUpdate = _appendOnUpdate;
        return c;
    }
};

}} // namespace test_wrappers::storage
